// 参考 https://github.com/stemkoski/stemkoski.github.com/blob/master/Three.js/Shader-Glow.html
import { 
  AdditiveBlending,
  AmbientLight,
  BoxGeometry,
  Color,
  DoubleSide,
  FrontSide,
  Mesh,
  MeshLambertMaterial,
  MeshPhongMaterial,
  MeshStandardMaterial,
  PerspectiveCamera,
  PlaneGeometry,
  RepeatWrapping,
  Scene, 
  ShaderMaterial, 
  SphereGeometry, 
  SpotLight, 
  SpotLightHelper, 
  TextureLoader, 
  WebGLRenderer
} from "three";
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls';
import stats from '../../utils/stats';
import TWEEN from '@tweenjs/tween.js';
import { LightLine } from './libs/lightLine';
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader";
import { DRACOLoader } from "three/examples/jsm/loaders/DRACOLoader";
import { SkeletonUtils } from 'three/examples/jsm/utils/SkeletonUtils'
import lineData from './data/lineData';
import createLabel from './createLabel';
import { GUI } from "dat.gui";

const scene = new Scene();
const camera = new PerspectiveCamera(45, window.innerWidth/window.innerHeight, 0.1, 1000);
camera.position.set(-80, 50, 110);
// camera.position.set(-17, 240, 30);
camera.lookAt(scene.position);
const renderer = new WebGLRenderer({ antialias: true });
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.shadowMap.enabled = true;
// renderer.setClearColor(0xffffff);
document.body.appendChild(renderer.domElement);

scene.add(new AmbientLight(0xffffff));

const spotLight = new SpotLight(0xffffff);
spotLight.position.set(0, 100, 0);
spotLight.castShadow = true;
scene.add(spotLight);
scene.position.y = -20;
// scene.add(new SpotLightHelper(spotLight));

const textureLoader = new TextureLoader();
const planeGeometry = new PlaneGeometry(200, 200);
const planeMaterial = new MeshPhongMaterial({
  color: 0xffffff,
});
planeMaterial.map = textureLoader.load('/factory/map.png');
planeMaterial.map.wrapS = RepeatWrapping;
planeMaterial.map.wrapT = RepeatWrapping;
const plane = new Mesh(planeGeometry, planeMaterial);
plane.receiveShadow = true;
plane.rotation.x = Math.PI/-2;
scene.add(plane);

const boxGeometry = new BoxGeometry(4, 4, 4);
const boxMaterial = new MeshStandardMaterial({
  color: 0xff9900
});
const box = new Mesh(boxGeometry, boxMaterial);
box.position.set(0, 2, 0);
// scene.add(box);

// 片元着色器
const fragmentShader = `
#include <common>

uniform vec3 glowColor;
varying float intensity;

void main() {
  vec3 glow = glowColor * intensity;
  gl_FragColor = vec4(glow, 1.0);
}
`;
const vertexShader = `
uniform vec3 viewVector;
uniform float c;
uniform float p;
varying float intensity;

void main() {
  vec3 vNormal = normalize(normalMatrix * normal);
  vec3 vNormel = normalize(normalMatrix * viewVector);

  intensity = pow(c - dot(vNormal, vNormel), p);
  gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
}
`
const sphereGeometry = new SphereGeometry(10, 50, 50, 0, Math.PI*2, 0, Math.PI/2);
const sphereMaterial = new MeshStandardMaterial({
  color: 0xffff00,
  transparent: true,
  opacity: 0.2,
  // 定义混合模式
  blending: AdditiveBlending,
  // depthTest: false,
  // alphaTest: 1,
  side: FrontSide,
});
const sphereShaderMaterial = new ShaderMaterial({
  vertexShader,
  fragmentShader,
  // transparent: true,
  // opacity: 0.2,
  // 定义混合模式
  blending: AdditiveBlending,
  // alphaTest: 1,
  // side: FrontSide,
  // depthTest: true,
  depthWrite: false,
  uniforms: {
    glowColor: { value: new Color(0xff0000) },
    c: { value: 1.0 },
    p: { value: 1.4 },
    viewVector: { value: camera.position }
  },
});
const sphere1 = new Mesh(sphereGeometry, sphereMaterial);
sphere1.position.x = 10;

const sphere2 = new Mesh(sphereGeometry, sphereShaderMaterial);
const sphere3 = new Mesh(sphereGeometry, sphereShaderMaterial);
sphere3.position.x = -10;

scene.add(sphere1);
scene.add(sphere2);
scene.add(sphere3);

// 道路光线
const lightLine = new LightLine(lineData, renderer, scene);
// 创建浮动标签
const labels = createLabel(scene);

const gltfLoader = new GLTFLoader();
gltfLoader.setCrossOrigin( 'anonymous' );
gltfLoader.setDRACOLoader(new DRACOLoader());

gltfLoader.load(
  '/models/lighthouse/lighthouse1.gltf',
  gltf => {
    let mesh = gltf.scene;
    let mate = new MeshPhongMaterial({
      color: 0x333333,
      transparent: true,
      opacity: 0.8
    });
    mesh.position.set(43, 0, -0.5);

    console.log(mesh.getObjectByName('ball'))

    mesh.traverse((o: any) => {
      if (o.isMesh) {
        // 单个元素材质调整
        if (o.name === 'ball') {
          o.material = new MeshStandardMaterial({
            color: 0xffffff,
            metalness: 1,
            roughness: 0.5
          });
        } else {
          o.material = mate;
        }
      }
    });

    scene.add(mesh);
  }
);

gltfLoader.load(
  '/example/test1.gltf',
  gltf => {
    let mesh = gltf.scene;
    let mate = new MeshStandardMaterial({
      color: 0x777777,
      metalness: 0.7,
      roughness: 0.5,
      transparent: true,
      opacity: 0.5,
      side: DoubleSide
    })

    mesh.traverse((o: any) => {
      if (o.isMesh) o.material = mate
    })

    mesh.scale.set(1.26, 1.26, 1.26);

    scene.add(mesh);
  }
);

const trackballControls = new OrbitControls(camera, renderer.domElement);
const controls = {
  labelVisible: true,
}
const gui = new GUI;
const labelGUI = gui.addFolder('浮动标签');
labelGUI.add(controls, 'labelVisible').name('显示/隐藏').onChange(val => {
  labels.forEach(group => {
    if (val) group.show();
    else group.hide();
  })
})

function animate() {
  new TWEEN.Tween({value: 0}).to({value: 1}, 2000)
    // https://sole.github.io/tween.js/examples/03_graphs.html
    .easing(TWEEN.Easing.Quadratic.Out)
    .onUpdate(({value}) => {
      sphere1.scale.set(value, value, value);
      sphere2.scale.set(value, value, value);
      sphere3.scale.set(value, value, value);
    })
    .start()
    .repeat(Infinity)
}

function render() {
  stats.update();
  TWEEN.update();
  trackballControls.update();

  lightLine.update();

  // console.log(camera.position)

  requestAnimationFrame(render);
  renderer.render(scene, camera);
}

render();
// animate();